//#!cscript //nologo

/*
Other test files should be run as:
//#!cscript //nologo DoTest.wsf

WScript.exe and CScript.exe Options
//U Used with Windows NT and Windows 2000 to force the command line output to be in Unicode. There is no way for CScript to determine whether to output in Unicode or ANSI; it defaults to ANSI.
//X Launches the program in the debugger.
//Job:<JobID> Runs the specified JobID from the .wsf file.
//T:nn Enables time-out: the maximum number of seconds the script can run.
*/

//---------------------------------------------------------------------------

/** This flag controls whether to show parameters of assert statements. */
var DoTest_showAssert;

/** Throw exception in fail() when this is true. */
var DoTest_usingThrow;

if ("undefined" == typeof fail) fail =
/** Display failure message. Can throw exception if alert is too bothering. */
function fail(failureMessage) {
  if (DoTest_usingThrow) {
    throw new Error(failureMessage);
    // Use this version when integrating with JsUnit:
    // throw new JsUnitException(null, failureMessage);
  }
  if ("undefined" != typeof WScript) {
    WScript.StdErr.WriteLine(failureMessage);
    WScript.Quit(1);
  } else {
    alert(failureMessage);
  }
  return false;
}

if ("undefined" == typeof info) info =
/**
 * Print info message to console.
 * Will write to document directly in loading phase.
 */
function info(infoMessage) {
  if ("undefined" != typeof WScript) {
    WScript.Echo(infoMessage);
  } else {
    if (window.console) {
      window.console.log(infoMessage);
    }
    // IE's readyState is special, it is "interactive" in document loading phase.
    if ("loading" == document.readyState
        || ("interactive" == document.readyState
            && "Microsoft Internet Explorer" == navigator.appName)) {
      infoMessage = "" + infoMessage;
      infoMessage = infoMessage.replace(/&/g, "&amp;");
      infoMessage = infoMessage.replace(/</g, "&lt;");
      infoMessage = infoMessage.replace(/>/g, "&gt;");
      document.writeln(infoMessage);
    }
  }
}

/**
 * If the argument is a function, its return value will be checked.
 */
function assertTrue(valueOrFunc) {
  var value = valueOrFunc;
  if ("function" == typeof valueOrFunc) {
    value = valueOrFunc();
  }
  if (value) {
    return true;
  }
  return fail("assertTrue fails: " + valueOrFunc + "\n----\n" + assertTrue.caller);
}

function NOTICE(valueOrFunc) {
  var value = valueOrFunc;
  if ("function" == typeof valueOrFunc) {
    value = valueOrFunc();
  }
  if (value) {
    return true;
  }
  return fail("NOTICE fails: " + valueOrFunc + "\n----\n" + NOTICE.caller);
}

function assertFalse(valueOrFunc) {
  var value = valueOrFunc;
  if ("function" == typeof valueOrFunc) {
    value = valueOrFunc();
  }
  if (value) {
    return fail("assertFalse fails: " + valueOrFunc + "\n----\n" +  assertFalse.caller);
  }
  return true;
}

function assertNever(valueOrFunc) {
  var value = valueOrFunc;
  if ("function" == typeof valueOrFunc) {
    value = valueOrFunc();
  }
  if (value) {
    return fail("assertNever fails: " + valueOrFunc + "\n----\n" +  assertNever.caller);
  }
  return true;
}

function ASSERT_NEVER(valueOrFunc) {
  var value = valueOrFunc;
  if ("function" == typeof valueOrFunc) {
    value = valueOrFunc();
  }
  if (value) {
    return fail("ASSERT_NEVER fails: " + valueOrFunc + "\n----\n" + ASSERT_NEVER.caller);
  }
  return true;
}

function assertEquals(expected, actual, message) {
  if (DoTest_showAssert) {
    info("assertEquals : " + expected + "," + actual);
  }
  if (expected == actual) {
    return true;
  }
  if (null == message) {
    message = "";
  }
  return fail("assertEquals(expected, actual) fails: " + message + "\n"
    + "arg1 : " + expected + "\n" + "arg2 : " + actual + "\n----\n"
    + assertEquals.caller);
}

function NOTICE_EQUALS(expected, actual, message) {
  if (DoTest_showAssert) {
    info("NOTICE_EQUALS : " + expected + "," + actual);
  }
  if (expected == actual) {
    return true;
  }
  if (null == message) {
    message = "";
  }
  return fail("NOTICE_EQUALS(expected, actual) fails: " + message + "\n"
    + "arg1 : " + expected + "\n" + "arg2 : " + actual + "\n----\n"
    + NOTICE_EQUALS.caller);
}

function assertNotEquals(expected, actual, message) {
  if (DoTest_showAssert) {
    info("assertNotEquals : " + expected + "," + actual);
  }
  if (null == message) {
    message = "";
  }
  if (expected == actual) {
    return fail("assertNotEquals fails: " + message + "\n"
      + "arg1 : " + expected + "\n" + "arg2 : " + actual + "\n----\n"
      + assertNotEquals.caller);
  }
  return true;
}

// ----

/** The test names to include. */
var DoTest_includes = "";
/** The test names to exclude. */
var DoTest_excludes = "";

/** This holds all functions to be tested. */
var DoTest_allTests;

/**
 * This is used to register a function to be tested.
 *
 * TODO: TESTCASE(theFunction, runner) to support instance test with setUp/tearDown.
 */
function TESTCASE(theFunction) {
  if (!DoTest_allTests) DoTest_allTests = [];
  DoTest_allTests.push(theFunction);
}

/**
 * This is a general test runner. It will executes all functions registered
 * by {@link TESTCASE}.
 */
function allTestsLocalRunner() {
  for (var i = 0; i < DoTest_allTests.length; ++i) {
    if (DoTest_includes.length != 0 || DoTest_excludes.length != 0) {
      var s = DoTest_allTests[i].toString();
      var n = s.indexOf("(");
      var funcName = '';
      if (-1 != n) {
        funcName = s.substring(0, n);
      }
      // Opera has an extra "\n" before function
      if (0 == funcName.indexOf("\n")) {
        funcName = funcName.substring("\n".length);
      }
      if (0 == funcName.indexOf("function ")) {
        funcName = funcName.substring("function ".length);
      }
      if (DoTest_includes.length != 0 && -1 == DoTest_includes.indexOf("," + funcName + ",")
          || DoTest_excludes.length != 0 && -1 != DoTest_excludes.indexOf("," + funcName + ",")) {
        continue;
      }
      info("  " + funcName);
    }
    try {
      DoTest_allTests[i]();
    } catch (e) {
      fail("" + e);
    }
  }
  // Clean up so that executed tests are not executed again.
  DoTest_allTests = [];
}
//---------------------------------------------------------------------------

/**
 * This is a sample test.
 */
TESTCASE(testAssert);
function testAssert() {
  var v;
  assertTrue(!false);
  assertFalse(v);
  assertEquals(18, 0x12);
  assertTrue(function() {return "a" < "b"});
}

// Put the following lines in the end of each test file.
//---------------------------------------------------------------------------
if ("function" == typeof allTestsLocalRunner) allTestsLocalRunner();
